// *************************************************************************************************
//
// MK3D Engine
// --------------------------------------
// Copyright (C) 2007-2011 Zeb (mailto: zebbey@gmail.com)
//
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//
// *************************************************************************************************

#include "mk_header.h"
#include "mk_d3d9_render_input.h"
#include "mk_engine.h"
#include "mk_d3d9_render_device.h"
#include "mk_error.h"
#include "mk_engine.h"
#include "mk_d3d9_hardware_buffer.h"

namespace MK3DE
{
	CD3D9RenderInput::CD3D9RenderInput() : _d3d9VertexDeclaration(NULL)
	{
	}

	CD3D9RenderInput::~CD3D9RenderInput()
	{
		SAFE_RELEASE(_d3d9VertexDeclaration);
	}

	BOOL CD3D9RenderInput::Create(RenderInputElemDesc* desc, DWORD descNum)
	{
		ASSERT(desc);
		ASSERT(descNum);

		BOOL ret = FALSE;
		MK_PROCESS_ERROR(descNum < MAX_RENDER_INPUT_ELEM_NUM);

		CD3D9RenderDevice *device = (CD3D9RenderDevice*)GetEngine()->GetRenderDevice();

		if (!_Create(desc, descNum))
			goto Exit0;

		D3DVERTEXELEMENT9 elem[MAX_RENDER_INPUT_ELEM_NUM + 1];
		DWORD i = 0;
		for (; i < descNum; i++)
		{
			if (!__MapRenderInputElemDesc(elem + i, desc + i))
				goto Exit0;
		}

		/*
		typedef struct _D3DVERTEXELEMENT9
		{
		WORD    Stream;     // Stream index
		WORD    Offset;     // Offset in the stream in bytes
		BYTE    Type;       // Data type
		BYTE    Method;     // Processing method
		BYTE    Usage;      // Semantics
		BYTE    UsageIndex; // Semantic index
		} D3DVERTEXELEMENT9, *LPD3DVERTEXELEMENT9;

		#define D3DDECL_END() {0xFF,0,D3DDECLTYPE_UNUSED,0,0,0}
		*/
		elem[i].Stream = 0xFF;
		elem[i].Offset = 0;
		elem[i].Type = D3DDECLTYPE_UNUSED;
		elem[i].Method = 0;
		elem[i].Usage = 0;
		elem[i].UsageIndex = 0;

		HRESULT hr = device->GetD3DDevice()->CreateVertexDeclaration(elem, &_d3d9VertexDeclaration);
		MK_PROCESS_COM_ERROR(hr);

		ret = TRUE;
Exit0:
		return ret;
	}

	BOOL CD3D9RenderInput::__MapRenderInputElemDesc(D3DVERTEXELEMENT9* pd3delem, RenderInputElemDesc* elem)
	{
		pd3delem->Stream = (WORD)elem->streamIndex;
		pd3delem->Offset = (WORD)elem->offset;
		pd3delem->Method = D3DDECLMETHOD_DEFAULT;
		pd3delem->UsageIndex = (BYTE)elem->usageIndex;
		switch (elem->usage)
		{
		case SU_POSITION:
			pd3delem->Type = D3DDECLTYPE_FLOAT3;
			pd3delem->Usage = D3DDECLUSAGE_POSITION;
			break;

		case SU_COLOR:
			pd3delem->Type = D3DDECLTYPE_D3DCOLOR;
			pd3delem->Usage = D3DDECLUSAGE_COLOR;
			break;

		case SU_NORMAL:
			pd3delem->Type = D3DDECLTYPE_FLOAT3;
			pd3delem->Usage = D3DDECLUSAGE_NORMAL;
			break;

		case SU_TEXCOORD:
			pd3delem->Type = D3DDECLTYPE_FLOAT2;
			pd3delem->Usage = D3DDECLUSAGE_TEXCOORD;
			break;

		case SU_POSITION_T:
			pd3delem->Type = D3DDECLTYPE_FLOAT4;
			pd3delem->Usage = D3DDECLUSAGE_POSITIONT;
			break;

		case SU_BLEND_INDICES:
			pd3delem->Type = D3DDECLTYPE_FLOAT4;
			pd3delem->Usage = D3DDECLUSAGE_TEXCOORD;
			break;

		case SU_BLEND_WEIGHT:
			pd3delem->Type = D3DDECLTYPE_FLOAT4;
			pd3delem->Usage = D3DDECLUSAGE_TEXCOORD;
			break;

		default:
			GetEngine()->GetLog()->Write(MKLT_ERROR, "Unknown render input element usage: %d", elem->usage);
			return FALSE;
		}

		return TRUE;
	}

	BOOL CD3D9RenderInput::BindVertexBuffer(LPDIRECT3DDEVICE9 pdevice)
	{
		for (DWORD i = 0; i < (DWORD)_vertexStream.size(); ++i)
		{
			CD3D9VertexHardwareBuffer* buffer = (CD3D9VertexHardwareBuffer*)_vertexStream[i];
			if (buffer)
			{
				HRESULT hr = pdevice->SetStreamSource((UINT)i, buffer->GetD3D9VertexBuffer(), 0, (UINT)buffer->GetStride());
				MK_PROCESS_COM_ERROR(hr);
			}
		}

		return TRUE;

Exit0:
		return FALSE;
	}

	BOOL CD3D9RenderInput::BindIndexBuffer(LPDIRECT3DDEVICE9 pdevice, BOOL& bIndex)
	{
		LPDIRECT3DINDEXBUFFER9 pib = NULL;
		if (_indexBuffer)
		{
			CD3D9IndexHardwareBuffer* buffer = (CD3D9IndexHardwareBuffer*)_indexBuffer;
			if (buffer)
				pib = buffer->GetD3D9IndexBuffer();
		}

		HRESULT hr = pdevice->SetIndices(pib);
		MK_PROCESS_COM_ERROR(hr);

		bIndex = pib == NULL ? FALSE : TRUE;

		return TRUE;
Exit0:
		return FALSE;
	}
}